﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Research.Kinect.Nui;

namespace KINECTUtility
{
    public class RuleDefinition{
        public int IndexHorizontal{get;set;}
        public int IndexVertical{get;set;}
        public enum ActionMode{
            Through,
            Occupied,
            Hold
        }
        public ActionMode HandAction{get;set;}
    }

    public class RuleChecker
    {
        private AreaChecker myAreaChecker;
        private JointID myJointID;
        private enum RuleCheckState
        {
            Ready,
            Tracking,
            OnRule,
            Completed,
            Failed
        }
        private RuleCheckState myState;

        public RuleChecker(AreaChecker checker)
        {
            myAreaChecker = checker;

            myUpdateEventHandler = new AreaChecker.AreaCheckerUpdateEventHandler(myAreaChecker_AreaMoved);
            myHoldEventHandler = new AreaChecker.AreaCheckerHoldEventHandler(myAreaChecker_AreaHold);

            myState = RuleCheckState.Ready;
        }

        private List<RuleDefinition> myRules = new List<RuleDefinition>();

        public void ClearRules()
        {
            myRules.Clear();
        }

        public void AddRule(int indexHorizontal, int indexVertical, RuleDefinition.ActionMode mode)
        {
            myRules.Add(new RuleDefinition()
            {
                IndexHorizontal = indexHorizontal,
                IndexVertical = indexVertical,
                HandAction = mode
            });
        }

        private AreaChecker.AreaCheckerHoldEventHandler myHoldEventHandler;
        private AreaChecker.AreaCheckerUpdateEventHandler myUpdateEventHandler;

        public void StartTracing(JointID jointId)
        {
            myJointID = jointId;
            myAreaChecker.AreaMoved += myUpdateEventHandler;
            myAreaChecker.AreaHold += myHoldEventHandler;
            ruleIndex = 0;
            myState = RuleCheckState.Tracking;
        }

        public bool IsTracking { get { return (myState == RuleCheckState.Tracking); } }
        public bool IsCompleted { get { return (myState == RuleCheckState.Completed); } }
        public bool IsFailed { get { return (myState == RuleCheckState.Failed); } }

        private int ruleIndex = 0;
        public int FilledStep { get { return ruleIndex; } }


        void myAreaChecker_AreaHold(AreaCheckerHoldEventArgs e)
        {
            if (myState == RuleCheckState.Tracking || myState == RuleCheckState.OnRule)
            {
                bool available = true;
                bool valid = false;
                if (myJointID == e.HoldJoint)
                {
                    if (ruleIndex > 0 && myState == RuleCheckState.OnRule)
                    {
                        if (myRules[ruleIndex - 1].HandAction == RuleDefinition.ActionMode.Occupied)
                        {
                            available = false;
                        }
                    }
                    if (available)
                    {
                        if (
                            (myRules[ruleIndex].HandAction == RuleDefinition.ActionMode.Hold
                            || myRules[ruleIndex].HandAction == RuleDefinition.ActionMode.Occupied)
                            && myRules[ruleIndex].IndexHorizontal == e.HoldIndexHorizontal
                            && myRules[ruleIndex].IndexVertical == e.HoldIndexVertical)
                        {
                            valid = true;
                        }
                        CheckRuleCompleted(valid);
                    }
                }
            }
        }

        private void CheckRuleCompleted(bool valid)
        {
            if (valid)
            {
                if (ruleIndex == 0)
                {
                    myState = RuleCheckState.OnRule;
                }
                if (RuleProgressed != null)
                {
                    RuleProgressed(this, ruleIndex);
                }
                ruleIndex++;
                if (myRules.Count == ruleIndex)
                {
                    myState = RuleCheckState.Completed;
                    myAreaChecker.AreaHold -= myHoldEventHandler;
                    myAreaChecker.AreaMoved -= myUpdateEventHandler;
                    if (RuleCompleted != null)
                    {
                        RuleCompleted(this);
                    }
                }
            }
            else
            {
                if (myState == RuleCheckState.OnRule)
                {
                    myState = RuleCheckState.Failed;
                    myAreaChecker.AreaHold -= myHoldEventHandler;
                    myAreaChecker.AreaMoved -= myUpdateEventHandler;
                }
            }
        }


        void myAreaChecker_AreaMoved(AreaCheckerUpdateEventArgs e)
        {
            if (myState == RuleCheckState.Tracking||myState== RuleCheckState.OnRule)
            {
                bool valid = false;
                if (myJointID == e.MovedJoint)
                {
                    if (myRules[ruleIndex].HandAction == RuleDefinition.ActionMode.Through
                        || myRules[ruleIndex].HandAction == RuleDefinition.ActionMode.Occupied
                    )
                    {
                        if (myRules[ruleIndex].IndexHorizontal == e.CurrentIndexHorizontal
                            && myRules[ruleIndex].IndexVertical == e.CurrentIndexVertical)
                        {
                            valid = true;
                        }
                    }
                    CheckRuleCompleted(valid);
                }
            }
        }

        public delegate void AreaRuleCheckerPerfectEventHandler(RuleChecker source);
        public delegate void AreaRuleCheckerProgressEventHandler(RuleChecker source, int currentStep);

        public event AreaRuleCheckerPerfectEventHandler RuleCompleted;
        public event AreaRuleCheckerProgressEventHandler RuleProgressed;
    }
}
